package java学习.第11章_常用类和基础API;

import org.apache.ibatis.annotations.One;
import org.luaj.vm2.ast.Str;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * 序列化是干啥用的？
 * 序列化的原本意图是希望对一个Java对象作一下“变换”，变成字节序列，这样一来方便持久化存储到磁盘，
 *  避免程序运行结束后对象就从内存里消失，另外变换成字节序列也更便于网络运输和传播，所以概念上很好理解：
 *      序列化：把Java对象转换为字节序列。
 *      反序列化：把字节序列恢复为原先的Java对象。
 * 而且序列化机制从某种意义上来说也弥补了平台化的一些差异，毕竟转换后的字节流可以在其他平台上进行反序列化来恢复对象。
 *
 * 在Java中，如果一个对象要想实现序列化，必须要实现下面两个接口之一：
 *      Serializable 接口
 *      Externalizable 接口
 *
 * 不想序列化的字段可以使用transient修饰。
 *
 */
public class L_序列化 {
    public static void main(String[] args) {
        Map<String, Object> map = new HashMap<>();

    }
}
/**
 * 由于Serializable对象完全以它存储的二进制位为基础来构造，因此并不会调用任何构造函数，因此Serializable类无需默认构造函数，
 * 但是当Serializable类的父类没有实现Serializable接口时，反序列化过程会调用父类的默认构造函数，因此该父类必需有默认构造函数，否则会抛异常。
 *
 *  使用transient关键字阻止序列化虽然简单方便，但被它修饰的属性被完全隔离在序列化机制之外，导致了在反序列化时无法获取该属性的值，
 * 而通过在需要序列化的对象的Java类里加入writeObject()方法与readObject()方法可以控制如何序列化各属性，甚至完全不序列化某些属性或者加密序列化某些属性。
 *
 * 1、凡是被static修饰的字段是不会被序列化的
 * 2、凡是被transient修饰符修饰的字段也是不会被序列化的
 *
 * serialVersionUID:
 * 1、serialVersionUID是序列化前后的唯一标识符
 * 2、默认如果没有人为显式定义过serialVersionUID，那编译器会为它自动声明一个！
 *
 *
 */
class MySerializable implements Serializable {
    @java.io.Serial
    private static final long serialVersionUID = 001L;//序列号

    private transient String kk = "不想序列化的字段";
}


/**
 * 1.它是Serializable接口的子类，用户要实现的writeExternal()和readExternal() 方法，用来决定如何序列化和反序列化。
 * 2.因为序列化和反序列化方法需要自己实现，因此可以指定序列化哪些属性，而transient在这里无效。
 * 3.对Externalizable对象反序列化时，会先调用类的无参构造方法，这是有别于默认反序列方式的。
 * 如果把类的不带参数的构造方法删除，或者把该构造方法的访问权限设置为private、默认或protected级别，
 * 会抛出java.io.InvalidException: no valid constructor异常，因此Externalizable对象必须有默认构造函数，而且必需是public的。
 */
class MyExternalizable implements Externalizable {
    public String name;
    public String age;
    public String bz;

    public MyExternalizable(String name, String age, String bz) {
        this.name = name;
        this.age = age;
        this.bz = bz;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeObject(age);
        out.writeObject(bz);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        age = (String) in.readObject();
        bz = (String) in.readObject();
    }
}
/**
 * 使用时，你只想隐藏一个属性，比如用户对象user的密码pwd，如果使用Externalizable，并除了pwd之外的每个属性都写在writeExternal()方法里，这样显得麻烦，
 * 可以使用Serializable接口，并在要隐藏的属性pwd前面加上transient就可以实现了。如果要定义很多的特殊处理，就可以使用Externalizable。
 *
 * 当然这里我们有一些疑惑，Serializable 中的writeObject()方法与readObject()方法 可以实现自定义序列化，
 * 而Externalizable 中的writeExternal()和readExternal() 方法也可以，他们有什么异同呢？
 *
 * readExternal(),writeExternal()两个方法，这两个方法除了方法签名和readObject(),writeObject()两个方法的方法签名不同之外，其方法体完全一样。
 * 需要指出的是，当使用Externalizable机制反序列化该对象时，程序会使用public的无参构造器创建实例，然后才执行readExternal()方法进行反序列化，
 * 因此实现Externalizable的序列化类必须提供public的无参构造。
 *
 * 虽然实现Externalizable接口能带来一定的性能提升，但由于实现ExternaLizable接口导致了编程复杂度的增加，
 * 所以大部分时候都是采用实现Serializable接口方式来实现序列化。
 *
 *
 */